package com.ccnode.codegenerator.taikaoback.service.impl;

import com.ccnode.codegenerator.taikaoback.security.jwt.JwtUtil;

import com.ccnode.codegenerator.taikaoback.service.TokenService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Token管理服务
 * 负责JWT token的生成、验证、存储和撤销
 */
@Slf4j
@Service
public class TokenServiceImpl implements TokenService {

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // Redis键前缀
    private static final String TOKEN_PREFIX = "jwt:token:";
    private static final String REFRESH_TOKEN_PREFIX = "jwt:refresh:";
    private static final String BLACKLIST_PREFIX = "jwt:blacklist:";
    private static final String USER_TOKEN_PREFIX = "jwt:user:";

    /**
     * 生成访问token和刷新token
     * @param username 用户名
     * @param userId 用户ID
     * @return token信息
     */
    public Map<String, Object> generateTokens(String username, String userId) {
        try {
            // 生成访问token
            Map<String, Object> claims = new HashMap<>();
            claims.put("userId", userId);
            String accessToken = jwtUtil.generateToken(username, claims);
            
            // 生成刷新token
            String refreshToken = jwtUtil.generateRefreshToken(username);
            
            // 存储token到Redis
            storeTokenInRedis(accessToken, username, userId, false);
            storeTokenInRedis(refreshToken, username, userId, true);
            
            // 存储用户当前token（用于单点登录控制）
            storeUserCurrentToken(username, accessToken);
            
            Map<String, Object> result = new HashMap<>();
            result.put("accessToken", accessToken);
            result.put("refreshToken", refreshToken);
            result.put("tokenType", "Bearer");
            result.put("expiresIn", 86400); // 24小时
            result.put("refreshExpiresIn", 604800); // 7天
            
            log.info("为用户 {} 生成token成功", username);
            return result;
            
        } catch (Exception e) {
            log.error("生成token失败: {}", e.getMessage());
            throw new RuntimeException("生成token失败", e);
        }
    }

    /**
     * 验证token是否有效
     * @param token JWT token
     * @return 是否有效
     */
    public boolean validateToken(String token) {
        try {
            // 首先检查token是否在黑名单中
            if (isTokenBlacklisted(token)) {
                log.warn("Token已被加入黑名单");
                return false;
            }
            
            // 验证JWT token本身
            if (!jwtUtil.validateToken(token)) {
                log.warn("JWT token验证失败");
                return false;
            }
            
            // 检查token是否在Redis中存在
            String tokenKey = TOKEN_PREFIX + token;
            Boolean exists = redisTemplate.hasKey(tokenKey);
            if (exists == null || !exists) {
                log.warn("Token在Redis中不存在");
                return false;
            }
            
            return true;
            
        } catch (Exception e) {
            log.error("验证token异常: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 刷新token
     * @param refreshToken 刷新token
     * @return 新的token信息
     */
    public Map<String, Object> refreshToken(String refreshToken) {
        try {
            // 验证刷新token
            if (!jwtUtil.validateToken(refreshToken)) {
                throw new RuntimeException("刷新token无效");
            }
            
            // 检查刷新token是否在Redis中存在
            String refreshTokenKey = REFRESH_TOKEN_PREFIX + refreshToken;
            Boolean exists = redisTemplate.hasKey(refreshTokenKey);
            if (exists == null || !exists) {
                throw new RuntimeException("刷新token不存在");
            }
            
            // 获取用户信息
            String username = jwtUtil.getUnameFromToken(refreshToken);
            Map<String, Object> tokenInfo = (Map<String, Object>) redisTemplate.opsForValue().get(refreshTokenKey);
            String userId = (String) tokenInfo.get("userId");
            
            // 生成新的访问token
            Map<String, Object> claims = new HashMap<>();
            claims.put("userId", userId);
            String newAccessToken = jwtUtil.generateToken(username, claims);
            
            // 存储新token到Redis
            storeTokenInRedis(newAccessToken, username, userId, false);
            
            // 更新用户当前token
            storeUserCurrentToken(username, newAccessToken);
            
            Map<String, Object> result = new HashMap<>();
            result.put("accessToken", newAccessToken);
            result.put("refreshToken", refreshToken); // 刷新token保持不变
            result.put("tokenType", "Bearer");
            result.put("expiresIn", 86400);
            
            log.info("用户 {} token刷新成功", username);
            return result;
            
        } catch (Exception e) {
            log.error("刷新token失败: {}", e.getMessage());
            throw new RuntimeException("刷新token失败", e);
        }
    }

    /**
     * 撤销token（登出）
     * @param token 要撤销的token
     * @param username 用户名
     */
    public void revokeToken(String token, String username) {
        try {
            // 将token加入黑名单
            addTokenToBlacklist(token);
            
            // 从Redis中删除token
            String tokenKey = TOKEN_PREFIX + token;
            redisTemplate.delete(tokenKey);
            
            // 清除用户当前token
            String userTokenKey = USER_TOKEN_PREFIX + username;
            redisTemplate.delete(userTokenKey);
            
            log.info("用户 {} 的token已撤销", username);
            
        } catch (Exception e) {
            log.error("撤销token失败: {}", e.getMessage());
            throw new RuntimeException("撤销token失败", e);
        }
    }

    /**
     * 撤销用户的所有token
     * @param username 用户名
     */
    public void revokeAllUserTokens(String username) {
        try {
            // 获取用户当前token
            String userTokenKey = USER_TOKEN_PREFIX + username;
            String currentToken = (String) redisTemplate.opsForValue().get(userTokenKey);
            
            if (currentToken != null) {
                // 将当前token加入黑名单
                addTokenToBlacklist(currentToken);
                
                // 删除token记录
                String tokenKey = TOKEN_PREFIX + currentToken;
                redisTemplate.delete(tokenKey);
            }
            
            // 清除用户token记录
            redisTemplate.delete(userTokenKey);
            
            log.info("用户 {} 的所有token已撤销", username);
            
        } catch (Exception e) {
            log.error("撤销用户所有token失败: {}", e.getMessage());
            throw new RuntimeException("撤销用户所有token失败", e);
        }
    }

    /**
     * 存储token到Redis
     */
    private void storeTokenInRedis(String token, String username, String userId, boolean isRefreshToken) {
        String keyPrefix = isRefreshToken ? REFRESH_TOKEN_PREFIX : TOKEN_PREFIX;
        String tokenKey = keyPrefix + token;
        
        Map<String, Object> tokenInfo = new HashMap<>();
        tokenInfo.put("username", username);
        tokenInfo.put("userId", userId);
        tokenInfo.put("createTime", System.currentTimeMillis());
        tokenInfo.put("isRefreshToken", isRefreshToken);
        
        // 设置过期时间
        long expireTime = isRefreshToken ? 604800 : 86400; // 刷新token 7天，访问token 24小时
        redisTemplate.opsForValue().set(tokenKey, tokenInfo, expireTime, TimeUnit.SECONDS);
    }

    /**
     * 存储用户当前token
     */
    private void storeUserCurrentToken(String username, String token) {
        String userTokenKey = USER_TOKEN_PREFIX + username;
        redisTemplate.opsForValue().set(userTokenKey, token, 86400, TimeUnit.SECONDS);
    }

    /**
     * 将token加入黑名单
     */
    private void addTokenToBlacklist(String token) {
        String blacklistKey = BLACKLIST_PREFIX + token;
        // 黑名单token保存时间与token过期时间一致
        long expireTime = jwtUtil.getExpirationDateFromToken(token).getTime() - System.currentTimeMillis();
        if (expireTime > 0) {
            redisTemplate.opsForValue().set(blacklistKey, true, expireTime, TimeUnit.MILLISECONDS);
        }
    }

    /**
     * 检查token是否在黑名单中
     */
    private boolean isTokenBlacklisted(String token) {
        String blacklistKey = BLACKLIST_PREFIX + token;
        Boolean exists = redisTemplate.hasKey(blacklistKey);
        return exists != null && exists;
    }

    /**
     * 获取token信息
     * @param token JWT token
     * @return token信息
     */
    public Map<String, Object> getTokenInfo(String token) {
        try {
            String tokenKey = TOKEN_PREFIX + token;
            return (Map<String, Object>) redisTemplate.opsForValue().get(tokenKey);
        } catch (Exception e) {
            log.error("获取token信息失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 检查用户是否已登录（是否有有效token）
     * @param username 用户名
     * @return 是否已登录
     */
    public boolean isUserLoggedIn(String username) {
        try {
            String userTokenKey = USER_TOKEN_PREFIX + username;
            String token = (String) redisTemplate.opsForValue().get(userTokenKey);
            return token != null && validateToken(token);
        } catch (Exception e) {
            log.error("检查用户登录状态失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 强制用户下线（撤销所有token）
     * @param username 用户名
     * @param reason 下线原因
     */
    public void forceUserOffline(String username, String reason) {
        try {
            // 检查用户是否已经被强制下线
            if (!isUserLoggedIn(username)) {
                log.info("用户 {} 已经不在线，无需强制下线", username);
                return;
            }
            
            revokeAllUserTokens(username);
            log.info("用户 {} 被强制下线，原因: {}", username, reason);
        } catch (Exception e) {
            log.error("强制用户下线失败: {}", e.getMessage());
            throw new RuntimeException("强制用户下线失败", e);
        }
    }

    /**
     * 获取在线用户数量（估算）
     * @return 在线用户数量
     */
    public long getOnlineUserCount() {
        try {
            // 使用scan代替keys以提高性能
            String pattern = USER_TOKEN_PREFIX + "*";
            long count = 0;
            // 简化处理，直接返回一个估算值
            return redisTemplate.keys(pattern).size();
        } catch (Exception e) {
            log.error("获取在线用户数量失败: {}", e.getMessage());
            return 0;
        }
    }
}